package com.ehotting.edsta.csharpServer.service.impl;

import com.alibaba.fastjson.JSON;
import com.ehotting.eaf.core.constant.HttpStatus;
import com.ehotting.eaf.core.domain.R;
import com.ehotting.eaf.core.enums.LicensePeriodEnum;
import com.ehotting.eaf.core.enums.LicenseStatus;
import com.ehotting.eaf.core.exception.ServiceException;
import com.ehotting.eaf.core.utils.*;
import com.ehotting.eaf.security.service.TokenService;
import com.ehotting.eaf.core.domain.csharpServer.LicenseSignedDto;
import com.ehotting.eaf.core.domain.csharpServer.LicenseApplyVo;
import com.ehotting.edsta.csharpServer.mapper.UserLicenseLogMapper;
import com.ehotting.edsta.csharpServer.mapper.UserLicenseMapper;
import com.ehotting.edsta.csharpServer.mapper.UserRsaMapper;
import com.ehotting.edsta.csharpServer.service.IUserLicenseService;
import com.ehotting.eaf.core.domain.system.api.UserLicense;
import com.ehotting.eaf.core.domain.system.api.UserLicenseLog;
import com.ehotting.eaf.core.domain.system.api.UserRsa;
import com.ehotting.eaf.core.domain.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;

/**
 * C端用户licenseService业务层处理
 *
 * @author edsta
 * @date 2023-03-24
 */
@Service
@Slf4j
public class UserLicenseServiceImpl implements IUserLicenseService {


    @Resource
    private UserLicenseMapper userLicenseMapper;

    @Autowired
    private TokenService tokenService;

    @Resource
    private UserRsaMapper userRsaMapper;

    @Resource
    private UserLicenseLogMapper userLicenseLogMapper;

    @Value("${aes.password}")
    private String aesPwd;

    /**
     * 查询C端用户license
     *
     * @param id C端用户license主键
     * @return C端用户license
     */
    @Override
    public UserLicense selectUserLicenseById(Long id) {
        return userLicenseMapper.selectUserLicenseById(id);
    }

    /**
     * 新增C端用户license
     *
     * @param userLicense C端用户license
     * @return 结果
     */
    @Override
    public int insertUserLicense(UserLicense userLicense) {
        return userLicenseMapper.insertUserLicense(userLicense);
    }

    /**
     * 生成license的业务逻辑处理
     * 1、组合基本信息
     * 2、创建RSA密钥对
     * 3、私钥签名
     * 4、AES加密签名
     * 5、生成license字符串
     *
     * @param licenseApplyVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateLicense(LicenseApplyVo licenseApplyVo) {
        String mcBoard = licenseApplyVo.getMcBoard();
        String mcCpu = licenseApplyVo.getMcCpu();
        String mcMac = licenseApplyVo.getMcMac();
        if (StringUtils.isAnyEmpty(mcBoard, mcCpu, mcMac)){
            throw new ServiceException("主机信息未提交");
        }
        LoginUser loginUser = tokenService.getLoginUser();
        Long userId = loginUser.getUserid();

        String machineCode = mcBoard + "+" + mcCpu + "+" + mcMac;
        UserLicense userLicenseByMachineCode = userLicenseMapper.selectUserLicenseByMachineCode(machineCode);
        if (userLicenseByMachineCode!=null && !machineCode.equals(userLicenseByMachineCode.getMachineCode())
                && LicenseStatus.NORMAL.getCode().equals(userLicenseByMachineCode.getStatus())){
            // 如果根据主机查询到信息，同时主机信息不一致，且该license为生效状态，则给出提示
            String username = userLicenseByMachineCode.getUsername();
            throw new ServiceException("该机器已成功绑定有效许可证，无需再次申请。\r账户【"+username+"】已与此机器关联，可直接使用本软件");
        }

        UserLicense userLicense = userLicenseMapper.selectUserLicenseByUserId(userId);
        if (userLicense!=null && LicenseStatus.NORMAL.getCode().equals(userLicense.getStatus())){
            throw new ServiceException("该用户已持有有效许可证，无需再次申请。\r请直接在其绑定的设备上使用我们的软件，享受顺畅体验。");
        }
        if (userLicense!=null
                && LicenseStatus.NOT_VERIFY_EXTEND.getCode().equals(userLicense.getStatus())
                && DateUtils.dateCompare(userLicense.getExpireTime(),new Date())>0){
            throw new ServiceException("该用户已持有有效许可证，无需再次申请。\r请直接在其绑定的设备上使用我们的软件，享受顺畅体验。");
        }
        if (userLicense!=null && !LicenseStatus.NORMAL.getCode().equals(userLicense.getStatus())){
            // 如果数据库有license，并且是未通过或者过期状态，则删除再新建
            userLicenseMapper.deleteUserLicenseById(userLicense.getId());
        }
        userLicense = new UserLicense();

        KeyGenerator.KeyDto keyStrDto = KeyGenerator.generate();
        if (keyStrDto ==null){
            throw new ServiceException("RSA密钥对生成失败，请联系管理人员");
        }

        String priStr = keyStrDto.getPriStr();
        String pubStr = keyStrDto.getPubStr();

        UserRsa userRsa = new UserRsa();
        userRsa.setRsaPublicKey(pubStr);
        userRsa.setRsaPrivateKey(priStr);
        userRsa.setUserId(userId);
        userRsaMapper.deleteUserRsaByUserId(userId);
        userRsaMapper.insertUserRsa(userRsa);

        Date startTime = new Date();
        int days = 10;
        userLicense.setStatus(LicenseStatus.NORMAL.getCode());
        if ("1".equals(licenseApplyVo.getLicenseType())) {
            // 如果是商用
            String period = licenseApplyVo.getPeriod();
            LicensePeriodEnum licensePeriodEnum = LicensePeriodEnum.getByDictValue(period);
            days = licensePeriodEnum.getDays();
            userLicense.setStatus(LicenseStatus.NOT_VERIFY.getCode());
        }

        /**测试代码，10分钟过期**/
        //Date expiredTime = DateUtils.addMinutes(startTime, days);
        Date expiredTime = DateUtils.addDays(startTime, days);

        // 返回c端的对象
        LicenseSignedDto licenseSignedDto = new LicenseSignedDto();
        licenseSignedDto.setExpiredTimeStr(DateUtils.daFormat(expiredTime));
        licenseSignedDto.setStartTimeStr(DateUtils.daFormat(startTime));
        licenseSignedDto.setAppName(licenseApplyVo.getAppName());
        licenseSignedDto.setAppVersion(licenseApplyVo.getAppVersion());
        licenseSignedDto.setLicenseType(licenseApplyVo.getLicenseType());
        licenseSignedDto.setFeatures(licenseApplyVo.getFeatures());
        licenseSignedDto.setRsaPuk(pubStr);
        licenseSignedDto.setMcMac(mcMac);
        licenseSignedDto.setMcCpu(mcCpu);
        licenseSignedDto.setMcBoard(mcBoard);

        try {
            // 使用rsa私钥对license内容签名
            String sign = RsaUtils.sign(priStr, licenseSignedDto.toString());
            // 使用aes密钥对签名进行加密
            String encrypt = AesUtils.encrypt(aesPwd, sign);
            licenseSignedDto.setSign(encrypt);
        } catch (Exception e) {
            throw new ServiceException("RSA签名失败，请联系管理人员");
        }

        userLicense.setLicenseType(licenseApplyVo.getLicenseType());
        userLicense.setMachineCode(machineCode);
        userLicense.setFeatureName(licenseApplyVo.getFeatures());
        userLicense.setExpireTime(expiredTime);
        userLicense.setStartTime(startTime);
        userLicense.setUserId(userId);
        userLicense.setUsername(loginUser.getUsername());
        userLicense.setLicense(JSON.toJSONString(licenseSignedDto));

        if (userLicense.getId()==null) {
            userLicense.setPhonenumber(licenseApplyVo.getPhonenumber());
            userLicense.setEnterpriseName(licenseApplyVo.getEnterpriseName());
            userLicenseMapper.insertUserLicense(userLicense);
        }else {
            userLicenseMapper.updateUserLicense(userLicense);
        }

    }

    /**
     * 校验license的业务逻辑处理
     *
     * @param licenseSignedDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<LicenseSignedDto> licenseVerify(LicenseSignedDto licenseSignedDto) {
        LoginUser loginUser = tokenService.getLoginUser();
        Long userId = loginUser.getUserid();

        UserRsa userRsa = userRsaMapper.selectUserRsaByUserId(userId);
        if (userRsa==null){
            saveLicenseLog(licenseSignedDto,null,"没有查到该用户的rsa公私钥");
            return R.fail("很抱歉，我们未能找到您的许可证信息。");
        }
        String savedRsaPuk = userRsa.getRsaPublicKey();

        UserLicense userLicense = userLicenseMapper.selectUserLicenseByUserId(userId);

        if (userLicense==null){
            saveLicenseLog(licenseSignedDto,null,"没有查到该用户的license");
            return R.fail("很抱歉，我们未能找到您的许可证信息。");
        }

        if (LicenseStatus.EXPIRED.getCode().equals(userLicense.getStatus())){
            // 如果状态被管理员设置过期，则直接返回
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"该license已失效");
            return R.fail(410,"该许可证已失效");
        }


        if (LicenseStatus.NOT_VERIFY.getCode().equals(userLicense.getStatus())){
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"该license尚未授权");
            return R.fail(412,"该许可证尚未授权");
        }
        String license = userLicense.getLicense();
        LicenseSignedDto result = JSON.parseObject(license, LicenseSignedDto.class);

        String rsaPuk = licenseSignedDto.getRsaPuk();
        if (StringUtils.isEmpty(rsaPuk)){
            // 如果C端提交的公钥为空，但是又已经审核通过的话，
            // 则表示C端尚未安装license文件，直接返回license对象给C端
            // 否则表示C端已安装license文件，将校验C端license信息的正确性
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"C端获取license信息");
            return R.ok(result,"成功获取license信息");
        }

        Date dbExpireTime = userLicense.getExpireTime();
        if (DateUtils.dateCompare(dbExpireTime,new Date())<0){
            // 校验数据库的license，时间过期
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"该license已过期");
            expireLicense(userLicense.getId());
            return R.fail(411,"该许可证已过期，请及时更新以确保您的服务能够继续正常使用。");
        }

        if (!savedRsaPuk.equals(rsaPuk)){
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"C端提交的公钥与数据库的不一致");
            return R.fail(415,"您提供的公钥与我们的数据库中的记录不匹配，请检查您的提交，确保准确性。");
        }

        if (!DateUtils.daFormat(dbExpireTime).equals(licenseSignedDto.getExpiredTimeStr())){
            // 校验数据库的过期时间和C端本地过期时间，不一致的话，提示更新
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"续费申请已获批准，提示更新许可证");
            return R.fail(414,"您的续费申请已获批准\r我们将会更新您的许可证文件\r以保持您的服务正常运行。");
        }

        String sign = licenseSignedDto.getSign();
        String decryptSign = AesUtils.decrypt(aesPwd, sign);
        String rsaPrivateKey = userRsa.getRsaPrivateKey();
        String priKeySign;
        try {
            priKeySign = RsaUtils.sign(rsaPrivateKey, licenseSignedDto.toString());
        } catch (Exception e) {
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"RSA签名失败:"+e.getMessage());
            return R.fail("RSA签名失败，请联系管理人员");
        }

        if (priKeySign==null){
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"获取签名失败，内部错误");
            return R.fail("获取签名失败，请联系管理人员");
        }

        if (!priKeySign.equals(decryptSign)){
            saveLicenseLog(licenseSignedDto,userLicense.getId(),"签名不一致");
            return R.fail(413,"签名不一致，请检查");
        }

        saveLicenseLog(licenseSignedDto,userLicense.getId(),"license校验成功");

        return R.ok(result);
    }

    private void expireLicense(Long id) {
        log.info("call expireLicense()>>>>");
        UserLicense userLicense = new UserLicense();
        userLicense.setId(id);
        userLicense.setStatus(LicenseStatus.EXPIRED.getCode());
        userLicenseMapper.updateUserLicense(userLicense);
    }


    /**
     * 保存授权记录
     * @param licenseSignedDto
     * @param userLicenseId
     * @param errMsg
     */
    private void saveLicenseLog(LicenseSignedDto licenseSignedDto,Long userLicenseId, String errMsg){
        log.info("saveLicenseLog>>>>>>");
        UserLicenseLog userLicenseLog = new UserLicenseLog();
        userLicenseLog.setApplyTime(new Date());
        if (licenseSignedDto==null){
            userLicenseLog.setNote(errMsg);
            userLicenseLogMapper.insertUserLicenseLog(userLicenseLog);
            return;
        }
        LoginUser loginUser = tokenService.getLoginUser();
        Long userid = loginUser.getUserid();
        String username = loginUser.getUsername();

        userLicenseLog.setUserId(userid);
        userLicenseLog.setUsername(username);
        userLicenseLog.setAppName(licenseSignedDto.getAppName());
        userLicenseLog.setFeatureName(licenseSignedDto.getFeatures());
        userLicenseLog.setLicenseId(userLicenseId);
        userLicenseLog.setLicenseType(licenseSignedDto.getLicenseType());
        if (StringUtils.isEmpty(errMsg)){
            userLicenseLog.setIsAuthenticated(1);
        }else {
            userLicenseLog.setNote(errMsg);
            userLicenseLog.setIsAuthenticated(0);
        }

        userLicenseLogMapper.insertUserLicenseLog(userLicenseLog);
    }
    @Override
    public Boolean isApplied() {
        LoginUser loginUser = tokenService.getLoginUser();
        Long userId = loginUser.getUserid();
        UserLicense userLicense = userLicenseMapper.selectUserLicenseByUserId(userId);
        // 不等于null
        return userLicense != null;
    }

    @Override
    public void licenseExtend(LicenseSignedDto licenseApplyVo) {
        LoginUser loginUser = tokenService.getLoginUser();
        Long userid = loginUser.getUserid();
        String username = loginUser.getUsername();
        UserLicense userLicense = userLicenseMapper.selectUserLicenseByUserId(userid);
        if (userLicense == null){
            throw new ServiceException("当前用户【"+username+"】尚未申请license");
        }
        Date expireTime = userLicense.getExpireTime();
        String period = licenseApplyVo.getPeriod();
        LicensePeriodEnum licensePeriodEnum = LicensePeriodEnum.getByDictValue(period);
        int days = licensePeriodEnum.getDays();
        userLicense.setExtendTime(DateUtils.addDays(expireTime, days));
        userLicense.setStatus(LicenseStatus.NOT_VERIFY_EXTEND.getCode());
        userLicenseMapper.updateUserLicense(userLicense);
    }
}